package com.fary.web.servlet.mvc.method.annotation;

import com.fary.core.Conventions;
import com.fary.core.MethodParameter;
import com.fary.core.SpringException;
import com.fary.core.annotation.AnnotatedElementUtils;
import com.fary.http.converter.HttpMessageConverter;
import com.fary.http.server.ServletServerHttpRequest;
import com.fary.http.server.ServletServerHttpResponse;
import com.fary.util.Assert;
import com.fary.validation.BindingResult;
import com.fary.web.accept.ContentNegotiationManager;
import com.fary.web.bind.WebDataBinder;
import com.fary.web.bind.annotation.RequestBody;
import com.fary.web.bind.annotation.ResponseBody;
import com.fary.web.bind.support.WebDataBinderFactory;
import com.fary.web.context.request.NativeWebRequest;
import com.fary.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;

public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {

	public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters) {
		super(converters);
	}

	public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters, ContentNegotiationManager manager) {
		super(converters, manager);
	}

	/**
	 * 解析参数调用此构造函数
	 */
	public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters, List<Object> requestResponseBodyAdvice) {
		super(converters, null, requestResponseBodyAdvice);
	}

	/**
	 * 解析返回值调用此构造函数
	 */
	public RequestResponseBodyMethodProcessor(List<HttpMessageConverter<?>> converters, ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
		super(converters, manager, requestResponseBodyAdvice);
	}


	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(RequestBody.class);
	}

	/**
	 * 1.类上有@ResponseBody注解
	 * 2.方法上有@ResponseBody注解
	 */
	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class));
	}

	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
								  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

		// 如果参数是Optional容器类型，则返回容器内部参数
		parameter = parameter.nestedIfOptional();

		// 读取请求，为参数创建一个参数值
		// parameter.getNestedGenericParameterType() 获取当前方法参数的嵌套泛型类型
		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
		// 获取参数的变量名，类似于user
		String name = Conventions.getVariableNameForParameter(parameter);

		// 数据绑定工厂
		if (binderFactory != null) {
			// 创建本次请求的数据绑定器
			WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
			if (arg != null) {
				// 校验数据
				validateIfApplicable(binder, parameter);
				if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
					throw new SpringException(parameter);
				}
			}
			/**
			 * MODEL_KEY_PREFIX=“org.springframework.validation.BindingResult.”
			 * 将绑定结果保存到模型中
			 */
			if (mavContainer != null) {
				mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
			}
		}

		// 适配给定参数值和方法参数
		// 这个方法主要用来处理参数类型是Optional，它会执行Optional.of(arg)方法将真实参数值包装为Optional容器
		return adaptArgumentIfNecessary(arg, parameter);
	}

	@Override
	protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter, Type paramType) throws IOException, SpringException{

		// 得到原始的request请求对象
		HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
		Assert.state(servletRequest != null, "No HttpServletRequest");
		// 构建一个服务请求对象
		ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
		// 解析请求体内容并进行类型转换
		Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
		if (arg == null && checkRequired(parameter)) {
			throw new SpringException("Required request body is missing: " + parameter.getExecutable().toGenericString());
		}
		return arg;
	}

	protected boolean checkRequired(MethodParameter parameter) {
		RequestBody requestBody = parameter.getParameterAnnotation(RequestBody.class);
		return (requestBody != null && requestBody.required() && !parameter.isOptional());
	}

	@Override
	public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, SpringException {
		// 设置请求已被处理
		mavContainer.setRequestHandled(true);
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		// 将返回值写入响应体输出流中
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}

}